home *** CD-ROM | disk | FTP | other *** search
/ CD Ware Multimedia 1995 May / cd Ware (Juegos) Epimundo.iso / DOS / C / COP.ZIP / COP.DOC < prev    next >
Encoding:
Text File  |  1992-07-23  |  42.1 KB  |  1,256 lines

  1. COP is shareware meaning try before you buy.  To use
  2. COP for any other purpose than for evaluation to 
  3. arrive at a purchase decision requires that you 
  4. register COP.  The fee is $35 US.  Upon registration
  5. you will be sent the latest version on 3.5 DOS formated
  6. diskette and hard copy documentation.
  7.  
  8. COP
  9.  
  10. The C Object Programming Style Manual
  11.  
  12. (c) Copyright John W. Small 1992
  13. All rights reserved
  14.  
  15. PSW / Power Software
  16. P.O. Box 10072
  17. McLean, VA 22102 8072 USA
  18. (703) 759-3838
  19.  
  20.  
  21. Introduction
  22.  
  23. The COP (C Object Programing) style manual prescribes a 
  24. systematic, formalized approach to OOP (Object Oriented 
  25. Programming) in C.  COP supports the concepts of 
  26. encapsulation, single and multiple inheritance 
  27. including virtual base classes, and polymorphism using 
  28. the C preprocessor.  Simply include the "cop.h" header 
  29. file in your source file and you are ready to begin 
  30. object orient programming in C.
  31.  
  32. Please keep in mind that COP is not intended as a 
  33. replacement for C++.  Rather its purpose is to present 
  34. an OOP conceptual platform to programmers forced by 
  35. project requirements to code in C.  The complexities of 
  36. rendering an OOP design in a non-OOP language such as C 
  37. are often insurmountable.  But with a uniform, 
  38. documented, and well understood style of incorporating 
  39. OOP concepts, the proposition becomes tenable.  It is 
  40. hoped that COP will help OOP concepts to find their way 
  41. into the C world and perhaps thereby hasten the 
  42. acceptance of C++ and OOP.
  43.  
  44. By applying COP's OOP implementation strategy to 
  45. assembly language we could just as well have had ALOP
  46. (Assembly Language Object Programming).  Learning COP
  47. strategy will indeed help you conceptualize the 
  48. implementation strategies of other OOP languages.  In
  49. fact it could very well start you on your way to 
  50. implementing your own OOP language.
  51.  
  52.  
  53. What is OOP (Object Orient Programming)?
  54.  
  55. OOP makes code more readily reusable and extensible 
  56. through the use of objects.  These objects are used to 
  57. describe real world objects lending an intuitive 
  58. approach to application design.  An object has an 
  59. internal state (data) and can react (behavior) to 
  60. simulus (message functions).  OOP changes the focus of 
  61. application design from functional decomposition to 
  62. objective behavioral analysis.  The former entails flow 
  63. charting or pseudo code while the latter entails object 
  64. identification and classification.  Historically, 
  65. advances in algorithmic (procedural) languages have 
  66. attempted to make machine abstracted syntheses of the 
  67. real world progressively less arcane.  OOP takes the 
  68. opposite approach of attempting to take real world 
  69. objects and respresent them on the machine.  OOP 
  70. languages should facilitate iterative design; 
  71. unfortuanately this is where C++ starts to fall down 
  72. without the aid of CASE tools, and COP of course fails 
  73. miserably.  Never the less, COP bridges a the gap 
  74. between the procedural C world and the OOP revelation
  75. of the C++ world.
  76.  
  77. OOP embodies the concepts of encapsulation, 
  78. inheritance, and polymorphism.  Encapsulation means 
  79. packaging data and its associated functions together in 
  80. a object (structure) thus controlling access and 
  81. enforcing protocol.  Objects can inherit (hierarchical) 
  82. the properties (data and functions) of other objects 
  83. thus allowing for the reuse of code in an extensible 
  84. fashion.  Objects in similar (polymorphic) behaviorial 
  85. categories (hierarchies) but with differing attributes 
  86. can be distinguished at run time via the process of run 
  87. time (late) binding of virtual functions (function 
  88. pointers) thereby encouraging highly abstracted coding 
  89. methods which save development time (only in the sense 
  90. of the overall picture), reduce code sizes and 
  91. maintenance efforts.  Undoubtly you have been using 
  92. some OOP techniques in your C coding all along without 
  93. really thinking about it in such a formalized fashion, 
  94. much like the early pioneer programmers started using a 
  95. structured flow regimen to eliminate spagetti code long 
  96. before formalized structure programming languages were 
  97. written.  After structure programming came data 
  98. structures and now the time has come to move on to 
  99. objects and OOP.
  100.  
  101.  
  102. Getting Started with COP
  103.  
  104. Since COP is really only a style of programming, a 
  105. header file, "cop.h", containing macros is all that's 
  106. necessary - there is no "cop.c" source file.  However, 
  107. boiler plate forms have been provided to help you 
  108. cookbook your own objects.  These are the cop.hfm and 
  109. cop.cfm files.  For example, let's say you want to 
  110. code an object called shape.  Copy cop.hfm to 
  111. shape.h and cop.cfm to shape.c and edit these new 
  112. files.  These files are extensively commented to 
  113. refresh your understanding of COP style rules outlined 
  114. in this manual.
  115.  
  116. The easiest way to learn the COP style is to see OOP 
  117. concepts implemented in C++ and compare it to the 
  118. corresponding COP code.  If you don't known C++ you'll 
  119. learn some along the way!
  120.  
  121.  
  122.  
  123. Automatic Typedef's
  124.  
  125.  
  126. In C++ a structure tag becomes the name of the 
  127. structure type just as if it had been typedef'ed, e.g.
  128.  
  129.  
  130.     struct MyStruct { ... };
  131.  
  132.     MyStruct foobar;  // okay in C++ but not in C!
  133.  
  134.  
  135. COP automatically typedef's structure tags as follows:
  136.  
  137.     struct_(MyStruct) { ... };
  138.  
  139.     MyStruct foobar;  /* COP's struct_() macro
  140.                  typedef'ed MyStruct
  141.                  automatically  */
  142.  
  143.  
  144. As a side note, C++ has an alternate form for comments 
  145. using the "//" to introduce comments that continue to 
  146. the end of the current line.
  147.  
  148.  
  149.  
  150. Encapsulation
  151.  
  152.  
  153. C++ allows the encapsulation of functions with the data 
  154. they operate on to create objects.  Object packaging is 
  155. an important first step to modularity and reuseability.
  156.  
  157.  
  158.     struct MyStruct {
  159.         int x, y;
  160.         int getx()  { return x; }
  161.         int gety()  { return y; }
  162.         void setx(int x);
  163.         void sety(int y);
  164.  
  165.     };
  166.  
  167.     void MyStruct::setx(int x)
  168.         { this->x = x; }
  169.  
  170.     void MyStruct::sety(int y)
  171.         { this->y = y; }
  172.  
  173.  
  174.     MyStruct foobar;
  175.  
  176.     foobar.setx(5);
  177.     foobar.sety(10);
  178.  
  179.     // output foobar coordinates
  180.  
  181.     cout << "(" << foobar.getx() << ","
  182.         << foobar.gety() << ")";
  183.  
  184.  
  185. Actually the functions aren't part of the structure but 
  186. they tell the C++ compiler that they are part of the 
  187. MyStruct interface.  C++ member functions defined 
  188. within the structure are said to be inline.  When 
  189. called, the compiler inserts their code inline instead 
  190. of setting up a function call.  Notice that out of line 
  191. member functions are defined with the structure's name 
  192. and the scope resolution operator, "::."  This prevents 
  193. confusion with same named member functions of different 
  194. structures.  You may be wondering where the "this" came 
  195. from in the setx() function.  All member functions have 
  196. an implicit "this" pointer formal parameter that points 
  197. to the structure that setx() is to operate on.  It is 
  198. only necessary in C++ to use the "this" pointer to 
  199. resolve naming conflicts.  The "x" formal parameter 
  200. masks MyStruct.x so "this" was used to access it.  
  201. Stream output looks strange to you if you're coming 
  202. from a C background.  You'll need to refer to a C++ 
  203. primer for more information.
  204.  
  205.  
  206. COP encapsulation style is as follows:
  207.  
  208.  
  209.     struct_(MyStruct) {
  210.         int x, y;
  211.     };
  212.  
  213.     #define MS_getx(thiS)  (1?(thiS)->x:0)
  214.     #define MS_gety(thiS)  (1?(thiS)->y:0)
  215.  
  216.     void MS_setx(MyStruct * thiS, x)
  217.         {  thiS->x = x; }
  218.  
  219.     void MS_sety(MyStruct * thiS, y)
  220.         { thiS->y = y; }
  221.  
  222.  
  223.     MyStruct foobar;
  224.  
  225.     MS_setx(&foobar,5);
  226.     MS_sety(&foobar,10);
  227.  
  228.     /* output foobar coordinates */
  229.  
  230.     printf("(%d,%d)",MS_getx(&foobar),
  231.         MS_gety(&foobar));
  232.  
  233.  
  234. As you know with C you can't place functions inside 
  235. structures nor can you have two functions with the same 
  236. name.  So COP style has you contract a structure's name 
  237. into a member function prefix of your choice that is 
  238. then prepended to member function names thereby 
  239. avoiding naming conflicts.  In order to simulate inline 
  240. functions, macros are of course used.  COP style has 
  241. you use the condition assignment statement, (1? 
  242. (thiS)->x:0), etc., to guarantee that you don't have an 
  243. lvalue that can be assigned to!  Out of line member 
  244. function definitions appear as you would expect them.  
  245. Of course you have to supply the implicit "thiS" 
  246. pointer since the C compiler isn't going to do it for 
  247. you like the C++ compiler does.  You may be wondering 
  248. why "thiS" has a capitalized "S".  It is COP style to 
  249. capitalize the last letter of names to indicate that 
  250. they are pointers.  Also some combination C/C++ 
  251. compilers get upset if you're compiling in C and have a 
  252. variable named "this." (I won't mention any names!)
  253.  
  254.  
  255.  
  256. Information Hiding
  257.  
  258.  
  259. Besides the hiding of static variables inside the files
  260. in which they occur, C++ has a special structure type
  261. called "class" in which all members, both data and 
  262. functions, are private unless otherwise specified.  
  263. The following excerpt from shape.cpp demonstrates:
  264.  
  265.  
  266.     class Shape {
  267.         unsigned x, y;
  268.     public:
  269.         ...
  270.         unsigned getx() { return x; }
  271.         unsigned gety() { return y; }
  272.         ...
  273.     };
  274.  
  275.  
  276. Here, x and y are private and the compiler will only 
  277. let member functions access them.
  278.  
  279. COP has only the honor system as shown before:
  280.  
  281.     struct_(Shape) {
  282.         /* private: */
  283.         unsigned x, y;
  284.         ...
  285.     };
  286.  
  287.     #define SH_getx(thiS)  (1?(thiS)->x:0)
  288.     #define SH_gety(thiS)  (1?(thiS)->y:0)
  289.  
  290.  
  291. Polymorphism
  292.  
  293. C++ allows you to specify the run time binding of 
  294. functions.  Perhaps you have seen or written a sort
  295. routine that takes a compare function pointer as an
  296. argument.  The qsort() function prototype in the 
  297. ANSI C stdlib.h file is a perfect example.  The idea
  298. here is that the sort function doesn't know until run
  299. time what compare function it will call.  This allowed
  300. you to write a generic sort function that is extensible
  301. to operate on any data type.  The sort routine is said
  302. to be polymorphic in that it can take on many forms 
  303. (of data) in a rather homogeneous behavioral pattern
  304. (that of sorting).
  305.  
  306. Let's expand this idea to objects (struct or class) 
  307. with member functions.  Suppose you wanted to write a
  308. graphics display library.  It would be nice if you 
  309. could write a generic shape structure with a member
  310. function that could be called to display whatever shape
  311. it happens to take on.  But the member function would
  312. really have to be a pointer since the data type that
  313. is to be displayed isn't yet known.  In C++ this 
  314. special type of member function is identified with the
  315. keyword "virtual."
  316.  
  317.  
  318.     class Shape {
  319.         unsigned x, y;
  320.     public:
  321.         ...
  322.         unsigned getx() { return x; }
  323.         unsigned gety() { return y; }
  324.         virtual void show(int xxpose = 0,
  325.             int yxpose = 0,
  326.             unsigned scale = 1) {}
  327.         ...
  328.     };
  329.  
  330.  
  331. The show() virtual function does nothing as inline
  332. coded here.  After all it doesn't know what kind of
  333. shape it is.  The C++ compiler uses this information
  334. to maintain an internal virtual function pointer table.
  335. The assignments used in the formal parameter list of 
  336. show() are default values.  If show() is invoked with
  337. no actual parameters the C++ compiler will 
  338. automatically supplies 0 for both x and y and 1 for
  339. scale.
  340.  
  341.  
  342. You guessed it,  With COP you have to maintain the 
  343. virtual function tables yourself.
  344.  
  345.  
  346.     /*  shape.h  */
  347.  
  348.     struct_vFt_forward(Shape);
  349.  
  350.     struct_(Shape) {
  351.     /* private: */
  352.         unsigned x, y;
  353.         polymorphic();
  354.         vFT_decl(Shape);
  355.     };
  356.  
  357.     struct_vFt(Shape) {
  358.         vF_decl(void,show,
  359.             (Shape * thiS, int xxpose,
  360.             int yxpose, unsigned scale));
  361.         ...
  362.     };
  363.  
  364.     vFt_decl(Shape,Shape);
  365.  
  366.     vf_decl(void,SH,SH,show,(Shape * thiS,
  367.         int xxpose, int yxpose,
  368.         unsigned scale));
  369.  
  370.  
  371.     /*  shape.c  */
  372.  
  373.     vFt_def(Shape,Shape)  =  {
  374.         vF_value(SH,SH,show),
  375.         ...
  376.     };
  377.  
  378.     vf_def(void,SH,SH,show,(Shape * thiS,
  379.         int xxpose, int yxpose,
  380.         unsigned scale))  {}
  381.  
  382.  
  383. The vFT_decl() macro must be included in any structure
  384. that introduces virtual member functions.  This macro
  385. expands to point to the table of virtual function 
  386. pointers for Shape.  Of course this pointer type is
  387. unknown unless we forward declare it with the 
  388. struct_vFt_forward() macro!  As you will see later,
  389. any structure that introduces or inherits virtual 
  390. member functions is polymorphic and must therefore 
  391. include the polymorphic() macro.
  392.  
  393. The virtual function table structure must be laid out
  394. also using the struct_vFt() macro.  Use the vF_decl()
  395. macro to declare the function pointers themselves 
  396. within the table.  You can always refer to the macros
  397. in cop.h to see what the parameters are.  The first 
  398. parameter for vF_decl() is the virtual function's 
  399. return type.  The second is the function's name, and 
  400. the third is its parenthesized formal parameter list.
  401. Note again that you must supply the "thiS" pointer as
  402. the first parameter in this list!
  403.  
  404. Next, the default virtual function table for Shape is 
  405. is declared using the vFt_decl() macro.  Its two 
  406. parameters are the overriding and spawning structures.
  407. Since this is the default table Shape is the overriding
  408. as well as the spawning structure.  The spawning 
  409. structure is the structure that introduces the virtual
  410. member function while the overriding structure is the 
  411. one that redefines the function to be meaningful for 
  412. itself.
  413.  
  414. Don't forget you must also declare the default virtual
  415. function itself using the vf_decl() macro.  The shape.c
  416. file must naturally include the virtual function table
  417. and virtual function definitions, i.e. vFt_def() and
  418. vf_def()!  Make a mental note of the various COP
  419. macros used here.  A capitalized last letter in an 
  420. identifier name indicates a pointer to the type 
  421. identified without the last letter capitalized.  For
  422. example, vF_decl() is a virtual function pointer 
  423. declaration while vf_def() is the virtual function 
  424. definition itself.  If vFt_decl() is the declaration 
  425. for a virtual function pointer table, what does 
  426. vFT_decl() signify?  You got it, a pointer to the table
  427. of virtual function pointers.
  428.  
  429. In memory a vFt looks something like:
  430.  
  431.  
  432.      instance                virtual function
  433.                     table
  434.  
  435.      -----------------         --------------------
  436.      |               |         |                  |
  437.      |  structType   |         |  structType_vFt  |
  438.      |               |         |                  |
  439.      |          (vFT)|-------->|   (vF)->         |
  440.      |   (descendanT)|_        |                  |
  441.      ----------------- \       --------------------
  442.                |
  443.               _|_
  444.               \|/
  445.                ,
  446.  
  447.  
  448. The pointer to the virtual function table is never
  449. NULL and thus not checked!  Likewise the virtual
  450. function pointers in the table themselves are never
  451. NULL and not checked.  This of course requires that
  452. you code your constructors/destructor as outline in
  453. cop.cfm!
  454.  
  455. The descendanT pointer is used by overriding vf's to
  456. locate derived structure components.  Any overriding
  457. vf must have the logic for traversing descendant
  458. chains.  All derived structures are necessarily
  459. polymorphic and must include the polymorphic() macro
  460. even if they don't introduce new vf's at their level.
  461.  
  462.  
  463. Wow!  What a chunck of code is required for COP 
  464. virtual functions that's done automatically by C++!
  465. Please, before you give up on COP consider this.  If
  466. you have to port a C++ OOPs designed program to C, COP
  467. can be a real lifesaver!  It took me nearly six months
  468. to devise and refine COP to its present form which can
  469. implement any AT&T C++ 3.0 semantic form except 
  470. exceptions!  If you have been putting off learning C++,
  471. by the time you finish this shape example you should be
  472. convinced of C++'s superior conceptual plane and how it
  473. makes life easier for the programmer in the long run.
  474. But even if you only code in C++, COP will make you 
  475. have greater appreciation for what the C++ compiler is 
  476. doing behind the scenes for you.
  477.  
  478.  
  479.  
  480. Automatic Initialization and Cleanup
  481.  
  482.  
  483. Before we can see how polymorphism and virtual 
  484. functions work in practice, we need to first touch on
  485. the C++ concept of initialization and cleanup.  The 
  486. idea in C++ is that data should be initialized upon 
  487. definition and cleaned up when no longer used.  Further 
  488. more these processes should be automated and not burden
  489. the programmer.  This entails the use of special
  490. member functions known as constructors and destructors.
  491.  
  492.  
  493.     class Shape {
  494.         unsigned x, y;
  495.     public:
  496.         Shape(unsigned x = 0, unsigned y = 0)
  497.             { this->x = x; this->y = y; }
  498.         unsigned getx() { return x; }
  499.         unsigned gety() { return y; }
  500.         virtual void show(int xxpose = 0,
  501.             int yxpose = 0,
  502.             unsigned scale = 1) {}
  503.         virtual ~Shape() {}
  504.     };
  505.  
  506.  
  507. The constructor member function has the same name as 
  508. its associated class.  The destructor likewise has the
  509. same name with a ~ (tilda) prefix.  Notice that neither 
  510. constructors nor destructors have a return types!
  511. Shape's constructor simply assigns x and y values.
  512. Since Shape has no pointers to dynamically allocated 
  513. memory, etc., there is nothing for the destructor to 
  514. do.  Shape's destructor here is defined as virtual 
  515. because some specific Shapes may have to be destructed
  516. which can be accomplished by overriding this 
  517. destructor's definition.  The correct destruct function
  518. is then bound at run time for the particular Shape at
  519. hand.
  520.  
  521.  
  522. Before proceeding with COP constructors/destructors,
  523. let's see how inheritance works in C++.  We're going to
  524. define a Circle which IS A Shape but we must also 
  525. overriding Shape::show() redefining it to display a 
  526. Circle. 
  527.  
  528.  
  529.     class Circle : public Shape  {
  530.     protected:
  531.         unsigned radius;
  532.     public:
  533.         Circle(unsigned radius
  534.             = (unsigned) getmaxy()/8,
  535.             unsigned x = 0, unsigned y = 0)
  536.             : Shape((x?x:radius),
  537.                 (y?y:radius))
  538.             { this->radius = radius; }
  539.         virtual void show(int xxpose = 0,
  540.             int yxpose = 0,
  541.             unsigned scale = 1)
  542.         {
  543.             circle((int)getx()+xxpose,
  544.                 (int)gety()+yxpose,
  545.                 (int)(radius*scale));
  546.         }
  547.     };
  548.  
  549.  
  550. We do this by defining the class Circle which is 
  551. derived publicly from the Shape class.  Being derived
  552. means that Circle inherits Shape's x and y variables 
  553. and Shape's getx() and gety() functions.  Being 
  554. publicly derived from shape means that any public 
  555. member of Shape is a public member of Circle, data or
  556. function it doesn't matter.  To Shape's x and y data,
  557. Circle adds radius in protected scope.  Protected means
  558. that any shape derived from Circle can access radius
  559. but other users of the Circle class can not.  In the
  560. shapes example PieSlice inherits Circle and is able
  561. to directly read its Circle inherited radius member.
  562. Even though Circle inherits Shape's x and y members it
  563. can't access them directly because they are private
  564. to Shape.  But the inherited getx() and gety() are
  565. public so Circle simply uses them to read the x and y
  566. values.  This scope hiding in C++ is very powerful in
  567. that by controlling access interfaces, coupling can be
  568. defined in a precise modular fashion. 
  569.  
  570. The Circle constructor adds radius to its formal
  571. parameter list with a default of getmaxy()/8, a BGI
  572. function.  The Circle constructor must first call the
  573. Shape constructor to initialize its Shape inherited 
  574. portions.  Notice that if the defaults for x and y are
  575. used, namely zero, that x and y are supplied values of
  576. radius.  This is to insure a default constructed Circle
  577. is entirely visible.  The Circle constructor itself 
  578. only has to initialize radius.
  579.  
  580. Circle overrides Shape::show() with its own definition.
  581. It simply reads its x and y location and transposes it
  582. by xxpose and yxpose respectly and scales it in a call
  583. to the BGI circle primitive.
  584.  
  585.  
  586. Without having to see the declaration for PieSlice we
  587. can still understand what's happening in the following
  588. code.  We are creating two Shapes and then displaying
  589. them:
  590.  
  591.  
  592.     Shape * S1 = new Circle();
  593.     Shape * S2 = new PieSlice();
  594.  
  595.     S1->show();
  596.     S2->show();
  597.  
  598.  
  599. The S1 shape calls Circle's show() to display the 
  600. circle.  The show() virtual function is run time bound
  601. to Circle::show().  S2's show() is run time bound to
  602. PieSlice::show() instead.  This polymorphic behavior 
  603. allows us to write code to display a collection of 
  604. shapes without having to know what those shapes are in
  605. advance just like we could write a sort for unknown 
  606. data.  C++ maintains the overriding virtual function 
  607. tables automatically.
  608.  
  609.  
  610. As usual, COP is a pain but with gain!  You have to 
  611. code constructors and destructors yourself.  There is 
  612. quite a bit more going on behind the scenes that the 
  613. C++ compiler is doing for you automatically which 
  614. you'll soon see.  But take heart, this is the last 
  615. tough section of COP and then things will start to come
  616. together! 
  617.  
  618.  
  619.     struct_vFt_forward(Shape);
  620.  
  621.     struct_(Shape) {
  622.     /* private: */
  623.         unsigned x, y;
  624.         polymorphic();
  625.         vFT_decl(Shape);
  626.     };
  627.  
  628.     struct_vFt(Shape) {
  629.         vF_decl(void,show,
  630.             (Shape * thiS, int xxpose,
  631.             int yxpose, unsigned scale));
  632.           vF_decl(void,destruct,(Shape * thiS,
  633.             unsigned nobj, int malloced));
  634.     };
  635.  
  636.     vFt_decl(Shape,Shape);
  637.  
  638.     vf_decl(void,SH,SH,show,(Shape * thiS,
  639.         int xxpose, int yxpose,
  640.         unsigned scale));
  641.     vf_decl(void,SH,SH,destruct,(Shape * thiS,
  642.         unsigned nobj, int malloced));
  643.  
  644.  
  645.     struct_initVFTs_decl(SH,(Shape * thiS, 
  646.         void * descendanT_0,
  647.         vFT_0_decl(Shape)));
  648.  
  649.     #define SH_malloc(nobj)  \
  650.         MALLOC(sizeof(Shape)*nobj)
  651.     #define SH_free(thiS)  FREE(thiS)
  652.  
  653.  
  654.     struct_init_decl(Shape,SH,_,
  655.         (Shape * thiS_0, unsigned nobj,
  656.             unsigned x, unsigned y));
  657.  
  658.     struct_destruct_decl(Shape,SH);
  659.  
  660.  
  661.     #define SH_init(thiS_0,nobj,x,y)  \
  662.         struct_init(SH,_,(thiS_0,nobj,x,y))
  663.     #define SH_init_default(thiS_0)  \
  664.         SH_init(thiS_0,1,0,0)
  665.     #define SH_new(nobj,x,y)  \
  666.         SH_init((Shape *)0,nobj,x,y)
  667.     #define SH_new_default()  \
  668.         SH_init_default((Shape *)0)
  669.     #define SH_destruct(thiS,nobj)  \
  670.         vf_runTimeBind(thiS,destruct,  \
  671.         (thiS,nobj,0))
  672.     #define SH_delete(thiS_0,nobj) \
  673.         if (thiS_0) vf_runTimeBind(thiS_0,  \
  674.         destruct,(thiS_0,nobj,1)); else
  675.  
  676.     #define SH_getx(thiS)  (1?(thiS)->x:0)
  677.     #define SH_gety(thiS)  (1?(thiS)->y:0)
  678.  
  679.     #define SH_show(thiS,xxpose,yxpose,scale)  \
  680.         vf_runTimeBind(thiS,show,  \
  681.         (thiS,xxpose,yxpose,scale))
  682.     #define SH_show_default(thiS)  \
  683.         SH_show(thiS,0,0,1)
  684.  
  685.  
  686. Okay the first thing new in this listing is the
  687. virtual destructor function pointer declaration in the
  688. virtual function table structure.  You must always 
  689. follow this form exactly for virtual destructors!  C++
  690. allows you to call a constructor and destructor 
  691. repeatedly for each cell of a vector of class 
  692. instances automatically.  With COP you have to tell it
  693. how many (nobj) instances there are!  Likewise you must
  694. tell the destructor whether the instance(s) need to be 
  695. freed via malloced.
  696.  
  697. The next thing suprising but then immediately obvious
  698. is the need to initialize the virtual function tables.
  699. The struct_initVFTs_decl() macro declares the function
  700. that does this job.
  701.  
  702. The SH_malloc() and SH_free() macros are used inside
  703. the constructor/destructor definitions which you'll see
  704. in a minute.  These macros allow you to redefine the
  705. heap manager on a structure by structure basis.  The
  706. MALLOC and FREE macros, defined in cop.h allow to 
  707. redefine the heap manager on a global basis.  These 
  708. macros correspond to C++ concept of overloading the
  709. new and delete operators, C++'s equivalent to malloc()
  710. and free().
  711.  
  712. The struct_init_decl() and struct_destruct_decl() 
  713. macros define the protected scope constructor and
  714. destructor for shape.  The "_" parameter allows for
  715. overloading constructors by allowing various naming
  716. suffixes.  There is no C++ equivalent of these 
  717. protected scope constructor/destructors.  COP uses 
  718. these to define a consistent, extensible base line.
  719. Notice that the constructor's parameter list has a 
  720. "thiS_0" parameter.  This means that the "thiS" 
  721. parameter may be passed a NULL value.  The COP style
  722. doesn't require a member function to check for NULL 
  723. unless it has a "thiS_0" formal parameter!  The reason
  724. for this is efficiency, especially inline macros.  When
  725. "thiS_0" is NULL in a call to the constructor, it 
  726. signals the constructor that the instance being 
  727. initialized must first be dynamically allocated.  
  728. Remember the destructor is signaled to free an instance
  729. by the malloced parameter.
  730.  
  731. Next, a series of public constructor/destructor macros
  732. defines the user interface to Shape.  The only way
  733. for COP to allow for default parameters is to code
  734. additional macros with default suffixes whose 
  735. definitions supply those defaults.  Notice that the 
  736. SH_destruct() public destructor doesn't free the 
  737. instance but SH_delete() does.
  738.  
  739. The SH_show() macro run time binds the virtual function
  740. for you.
  741.  
  742.  
  743. Before we look at the source definitions for Shape's
  744. member functions we need to consider how COP copes
  745. with virtual function table initialization in an
  746. extensible fashion.  All this is handled for you 
  747. automatically by the C++ compiler and if you are 
  748. already a C++ programmer chances are you never really 
  749. thought about how the vFTs were handled.
  750.  
  751.  
  752. The ordering and internal steps of constructor and
  753. destructor calls are always coded as follows to insure
  754. extensibility:
  755.  
  756.  
  757.  
  758. base struct
  759.   ----------      --------------      --------------
  760.   |        |  2   |            |   1  |            |
  761.   |  init  |----->|  initVFTs  |<-----|  destruct  |
  762.   |        |      |            |      |            |
  763.   | 3 data |      |            |      |  2 data    |
  764.   ----------      --------------     _--------------
  765.      /|\    \          /|\           /|    /|\
  766.       |      \__________|___________/       |
  767.       | 1               |                   | 3
  768. derive|d struct         |                   |
  769.   ----------      --------------      --------------
  770.   |        |  2   |            |   1  |            |
  771.   |  init  |----->|  initVFTs  |<-----|  destruct  |
  772.   |        |      |            |      |            |
  773.   | 3 data |      |            |      |  2 data    |
  774.   ----------      --------------     _--------------
  775.      /|\    \          /|\           /|   /|\
  776.       |      \__________|___________/      |
  777.       | 1               |                  | 3
  778.       |                 |                  |
  779.   ----------      --------------      --------------
  780.   |        |  2   |            |   1  |            |
  781.   |  init  |----->|  initVFTs  |<-----|  destruct  |
  782.   |        |      |            |      |            |
  783.   | 3 data |      |            |      |  2 data    |
  784.   ----------      --------------     _--------------
  785.         \                        /|
  786.          \______________________/
  787.  
  788.  
  789.  
  790.  
  791. To read the chart imagine the base struct is Shape.
  792. The intermediate derived struct is then Circle and
  793. the lowest is PieSlice.  When PieSlice::init() 
  794. (constructor) is called it first calls its base class
  795. constructor, Circle::init().  After Circle::init() 
  796. returns, PieSlice::init() calls PieSlice::initVFTs()
  797. to initialize the hierarchy's virtual function tables
  798. to read properly for the PieSlice level.  Once that
  799. is done, PieSlice::init() initializes its data.  You
  800. should be aware that PieSlice::initVFTs() calls its
  801. base class initVFTs().  Besides initializing vFT 
  802. pointers, the descendanT pointer chain is also 
  803. established.  The show() virtual function that is bound
  804. at run time traverses this descendanT pointer chain to
  805. get to the derived data.  The polymorphic() macro you
  806. saw earlier expands inserting descendanT pointers
  807. in the various structures.  Later on you will see a
  808. diagram of structures with inherited structures.
  809.  
  810. On the destructor side of the house, 
  811. PieSlice::destruct() first calls initVFTs() to 
  812. reinitialize the vFTs to the current PieSlice level
  813. defaults, then destructs the PieSlice level data and
  814. calls Circle::destruct() to destruct lower levels.  
  815. Circle::destruct() reinitializes the vFTs to the Circle
  816. level.  After all we don't want PieSlice vFs to be 
  817. called after PieSlice data has since been destructed!
  818.  
  819. Before choking on the next source listing, remember 
  820. that cop.hfm and cop.cfm provide skeletal source forms
  821. which you can edit to greatly simplifing the task of 
  822. generating the source you are about to consider.  And 
  823. now for Shape member function source definitions found
  824. in shape.c interspersed with additional comments:
  825.  
  826.  
  827. /*  Shapes default vFt definition  */
  828.  
  829. vFt_def(Shape,Shape)  =  {   
  830.     vF_value(SH,SH,show),
  831.     vF_value(SH,SH,destruct)
  832. };
  833.  
  834.  
  835. /*  Shape::show() default definition  */
  836.  
  837. /* ARGSUSED */
  838. #pragma argsused
  839. vf_def(void,SH,SH,show,(Shape * thiS, int xxpose,
  840.     int yxpose, unsigned scale))
  841.     {}
  842.  
  843.  
  844. /*  Shape::~Shape()'s protected virtual function 
  845.     The public destructor was defined before in  
  846.     the two macros, SH_destruct() and SH_delete().
  847.     Notice the use of COP's base line protected
  848.     constructors/destructor throughout the rest
  849.     of this listing.
  850. */
  851.  
  852. vf_def(void,SH,SH,destruct,(Shape * thiS, 
  853.     unsigned nobj, int malloced))
  854. {
  855.     struct_destruct(SH,thiS,nobj,malloced);
  856. }
  857.  
  858.  
  859. /*  Protected vFT initializer function which has no
  860.     external C++ equivalent.
  861. */
  862.  
  863. struct_initVFTs_def(SH,(Shape * thiS,
  864.     void * descendanT_0, vFT_0_decl(Shape)))
  865. {
  866.     /*  Establish the descendanT chain, the last
  867.         level has no descendanT!
  868.     */
  869.     poly_assign(thiS,descendanT_0);
  870.  
  871.     /*  Since initVFTs() can be called by a 
  872.         derived class' initVFTs() it must be
  873.             decided whether to assign the current
  874.         level default vFT or a derived level's
  875.         vFT.
  876.     */
  877.     vFT_assign(Shape,Shape,
  878.         thiS,vFT_0_name(Shape));
  879. }
  880.  
  881.  
  882. /*  This Shape::Shape() protected function is a COP 
  883.     base line constructor (no C++ equivalent).  The 
  884.     public Shape::Shape() constructors were defined
  885.     above in the SH_init() macro series.
  886.  
  887.     Referring back to the constructor/destructor
  888.     call chart and the code below, notice that
  889.     the struct_init() can be called either directly
  890.     as a constructor for the instance at hand or as an
  891.     intermediately constructor to initialize a base
  892.     class.  Therefore the code must be written to
  893.     handle both situations.  When called as an instance
  894.     constructor, nobj will be >= 1 and thiS_0 may be 
  895.     NULL.  But when called as an intermediate level
  896.     constructor, nobj is always 1 and thiS_0 is never
  897.     NULL!  When nobj > 1, acting as an instance 
  898.     constructor it calls the intermediate level 
  899.     constructors repeatedly with nobj = 1 for each cell
  900.     of the vector.
  901. */
  902.  
  903. struct_init_def(Shape,SH,_,
  904.     (Shape * thiS_0, unsigned nobj,
  905.     unsigned x, unsigned y))
  906. {
  907.   unsigned i;
  908.  
  909.   if (!nobj)
  910.     return (Shape *)0;
  911.  
  912. /*  If thiS_0 == NULL this is an instance level 
  913.     constructor that needs to first allocate the 
  914.     instance at hand.
  915. */
  916.   if (!thiS_0) if ((thiS_0 = (Shape *)
  917.     SH_malloc(nobj))
  918.     == (Shape *)0)
  919.     return (Shape *)0;
  920. /*
  921.     If nobj > 1 this is again an instance level
  922.     constructor call for a vector of instances
  923.     so loop through each cell calling its next lower
  924.     intermediate level constructor on a cell by cell
  925.     basis.  After the next lower level returns 
  926.     reinitialize the vFT hierarchy for the current 
  927.     level and then initialize the data for the current
  928.     level.  Since Shape has no base classes the first
  929.     step of calling base class constructors doesn't
  930.     appear here.
  931. */
  932.   for (i = 0; i < nobj; i++)  {
  933.     struct_initVFTs(SH,(&thiS_0[i],(void *)0,
  934.     vFT0(Shape)));
  935.     thiS_0[i].x = x;
  936.     thiS_0[i].y = y;
  937.  
  938.   }  /* for */
  939.   return thiS_0;
  940. }
  941.  
  942. /*  This Shape::~Shape() protected function is a COP
  943.     base line destructor (no C++ equivalent).  Remember
  944.     there is a protected virtual destructor definition
  945.     of Shape::~Shape() above which calls this non 
  946.     virtual destructor.  This COP base line function
  947.     provides for a consistent destructor format 
  948.     regardless of whether or not the destructor is 
  949.     virtual.  It also provides a consistent basis for
  950.     overriding the protected virtual destructor (see
  951.     cop.cfm structType virtual destructor comments).
  952.  
  953.     If you look at the macro definition for 
  954.     struct_destruct_def() in cop.h you will see to it
  955.     expands into  PT_SH_Shape_destruct(Shape * thiS_0,
  956.     unsigned nobj, int malloced).  The PT_ prefix means
  957.     that the function is protected scope.  PV_ can be
  958.     used for private scope.
  959.  
  960.     Anyway, by again referring to the destructor 
  961.     hierarchy call diagram you can see this destructor
  962.     first reinitializes the vFT hierarchy for the 
  963.     current level, destructs the data at the current 
  964.     level, and then calls the base class destructors.
  965.     Shape has no data or base classes that need 
  966.     destructing so those steps have been left out.
  967.  
  968.     Like the constructor, nobj can only be > 1 and
  969.     malloced != 0 in calls to instance level 
  970.     destructors.  When called from a derived level,
  971.     nobj is always equal to 1 and malloced is equal
  972.     to 0!  After all, we don't an intermediate level
  973.     destructor freeing the instance!
  974. */
  975.  
  976. struct_destruct_def(Shape,SH)
  977. {
  978.   unsigned i;
  979.  
  980.   if (!thiS_0 || !nobj)
  981.     return;
  982.  
  983.   for (i = nobj; i--; /* no reinit */)  {
  984.     struct_initVFTs(SH,(&thiS_0[i],
  985.       (void *)0,vFT0(Shape)));
  986.   }
  987.  
  988.   if (malloced)
  989.     SH_free(thiS_0);
  990. }
  991.  
  992.  
  993. Two more shapes you need to know about in the shapes
  994. example are Rectangle and Segment.  Rectangle is 
  995. straight forward similar to Circle or PieSlice.  A 
  996. Segment is a collection of Shapes which is in a sense
  997. a Shape itself.  The Segment C++ class declaration is:
  998.  
  999.  
  1000.     class Segment : public Shape  {
  1001.         Shape ** shapes;
  1002.         unsigned shapeCount;
  1003.     public:
  1004.         Segment(unsigned x, unsigned y,
  1005.             unsigned shapeCount,
  1006.             .../* shapes */);
  1007.     // All shapes must be dynamically allocated!
  1008.     // Segment then owns these shapes for deletion
  1009.     // purposes.
  1010.         virtual void show(int xxpose = 0,
  1011.             int yxpose = 0,
  1012.             unsigned scale = 1);
  1013.         ~Segment();
  1014.     };
  1015.  
  1016.  
  1017. Segment::show() calls Shape::show() for each of its
  1018. internal shapes transposing them to the Segment's x,y
  1019. coordinates and any additional transpose and scale.  
  1020. The Segment's destructor calls each of its subshape's
  1021. destructor.  Then it deletes its own internal shapes
  1022. vector.
  1023.  
  1024. You can view the COP source in shape.h and shape.c. 
  1025.  
  1026. Be sure to make a comparison study of the shapes
  1027. example contrasing the C++ version with the C
  1028. version.  If you have the Borland C++ compiler go
  1029. ahead and run the two versions.  To compile:
  1030.  
  1031.   bcc shapes.cpp shape.cpp rectpie.cpp graphics.lib
  1032.  
  1033. or
  1034.  
  1035.   bcc shapes.c shape.c rectpie.c graphics.lib
  1036.  
  1037.  
  1038. You should take note that shape.c/pp has the Shape, 
  1039. Segment, and Circle declarations/definitions.  The 
  1040. shapes.c/pp main program loop focuses processing around
  1041. the Segment object.  This illustrates another important
  1042. feature of OOP.  The rectpie.c/pp was able to extend 
  1043. the Shapes that could be processed within a Segment to
  1044. include the Rectangle and PieSlice without recompiling
  1045. the shape.c/pp file.  This is an example of object 
  1046. extensibility.  Normally with C, a tagged union 
  1047. structure with a case statement would be used to
  1048. process various shapes.  If new shapes had to be added
  1049. the original case statement would have had to be 
  1050. modified and recompiled.  This is not the case with the
  1051. polymorphic object approach shown here with Segments!
  1052. The reusability of the Segment code was greatly 
  1053. enhanced along with productivity in creating those
  1054. extensions.
  1055.  
  1056.  
  1057. An additional subtle feature of C++ is implicit type
  1058. conversion.  With COP there is no such thing as 
  1059. implicit.  Consider the C++ excerpt from shapes.cpp,
  1060. the main program loop, and the corresponding C excerpt
  1061. from shapes.c:
  1062.  
  1063.  
  1064.     // shapes.cpp
  1065.  
  1066.     Segment s = Segment(0,0,3,
  1067.             new Circle(),
  1068.             new Rectangle(),
  1069.             new Segment(20,20,2,
  1070.                 new PieSlice(),
  1071.                 new Rectangle()
  1072.                 )
  1073.              );
  1074.  
  1075.     s.show();
  1076.     s.show(getmaxx()/2, getmaxy()/2, 2);
  1077.  
  1078.  
  1079.     /*  shapes.c  */
  1080.  
  1081.     Segment s;
  1082.  
  1083.     (void) SG_init(
  1084.       (&s,1,0,0,3,
  1085.         CR_ShapeThiS_0(CR_new_default()),
  1086.         RT_ShapeThiS_0(RT_new_default()),
  1087.         SG_ShapeThiS_0(SG_init(
  1088.           ((Segment *)0,1,20,20,2,
  1089.         PS_ShapeThiS_0(PS_new_default()),
  1090.         RT_ShapeThiS_0(RT_new_default())
  1091.           )
  1092.         ))
  1093.       )
  1094.     );
  1095.  
  1096.     SG_show_default(&s);
  1097.     SG_show(&s,getmaxx()/2,getmaxy()/2,2);
  1098.  
  1099.  
  1100. The C++ version implicitly typecasts the Circle, 
  1101. Rectangle, and Segment pointers to Shape pointers.  
  1102. The COP version has to call typecast functions to do
  1103. the conversions.  It's not as simple as just retyping
  1104. a pointer.  The pointer actually has to point to a 
  1105. different part of the object.
  1106.  
  1107. It's time to look at what structures with inherited
  1108. structures look like.  
  1109.  
  1110. Consider the following COP code for structure
  1111. inheritance and the depicting diagram
  1112. (vFTs not shown, note that base3 is virtual!):
  1113.  
  1114.  
  1115. struct_(base11) { polymorphic(); ... };
  1116. struct_(base12) { polymorphic(); ... };
  1117. struct_(base1)  {
  1118.     inherit(base11);
  1119.     inherit(base12);
  1120.     polymorphic();
  1121.     ...
  1122. };
  1123. struct_(base2)  { polymorphic(); ... };
  1124. struct_(base3)  { polymorphic(); ... };
  1125. struct_(structType)  {
  1126.     inherit(base1);
  1127.     inherit(base2);
  1128.     vbInherit(base3);
  1129.     polymorphic();
  1130.     ...
  1131. };
  1132.  
  1133. struct_vbc(structType)  {   // casing struct
  1134.     vbh_decl(structType);
  1135.     vb_decl(base3);
  1136. };
  1137.  
  1138.  
  1139.  
  1140.   -----------------------------------------------------
  1141.   |                                                   |
  1142.   |                                                   |
  1143.   |   ----------------------------                    |
  1144.   |   |                          |                    |
  1145.   |   |  ----------------------  |                    |
  1146.   |   |  |                    |  |                    |
  1147.   |   |  |   --------------   |  |                    |
  1148.   |   |  |   |            |   |  |                    |
  1149.   |   |  |   |   base11   |   |  |                    |
  1150.   |   |  |   |(descendanT)|-->|  |                    |
  1151.   |   |  |   --------------   |  |                    |
  1152.   |   |  |                    |  |                    |
  1153.   |   |  |   --------------   |  |                    |
  1154.   |   |  |   |            |   |  |                    |
  1155.   |   |  |   |   base12   |   |  |                    |
  1156.   |   |  |   |(descendanT)|-->|  |                    |
  1157.   |   |  |   --------------   |  |                    |
  1158.   |   |  |                    |  |                    |
  1159.   |   |  |                    |  |                    |
  1160.   |   |  |      base1         |  |                    |
  1161.   |   |  |        (descendanT)|->|                    |
  1162.   |   |  ----------------------  |                    |
  1163.   |   |                          |                    |
  1164.   |   |                          |                    |
  1165.   |   |      --------------      |                    |
  1166.   |   |      |            |      |                    |
  1167.   |   |      |   base2    |      |                    |
  1168.   |   |      |(descendanT)|----->|                    |
  1169.   |   |      --------------      |                    |
  1170.   |   |                          |                    |
  1171.   |   |        (host)            |                    |
  1172.   |   |      structType          |                    |
  1173.   |   |                          |    --------------  |
  1174.   |   |               (base3BasE)|--->|            |  |
  1175.   |   |                          |    |   base3    |  |
  1176.   |   |                          |<---|(descendanT)|  |
  1177.   |   ----------------------------    --------------  |
  1178.   |                                                   |
  1179.   |          structType casing struct                 |
  1180.   |                                                   |
  1181.   -----------------------------------------------------
  1182.  
  1183.  
  1184.  
  1185. If structType were inherited by a derivedStructType 
  1186. then base3BasE would still point to base3 except base3
  1187. would be in the derivedStructType casing struct and
  1188. base3.descendanT would point to the derivedStructType
  1189. host.
  1190.  
  1191. I snuck in multiple inheritance and virtual base 
  1192. classes on you here.  This contrived structure 
  1193. hierarchy appears in cop.hfm and cop.cfm with detailed
  1194. comments explaining the COP implementation strategy.
  1195. Instead of trying to explain multiple inheritance with
  1196. various level vFt overrides here, go ahead and jump in
  1197. by cloning cop.hfm and cop.cfm and follow the comments.
  1198. The only way it is going to stick in your mind anyway 
  1199. is to get your hands dirty.  Also described in the 
  1200. cop.?fm files are static class members, C++ template
  1201. emulation, operator overloading conventions, and many
  1202. subjects only briefly touch upon in this documentation.
  1203.  
  1204. Your questions, comments, and suggestions are always
  1205. welcome.  Happy OOPing!
  1206.  
  1207. John
  1208. CIS 73757,2233
  1209. (703) 759-3838
  1210.  
  1211.  
  1212.          ----------------end-of-author's-documentation---------------
  1213.  
  1214.                          Software Library Information:
  1215.  
  1216.                     This disk copy provided as a service of
  1217.  
  1218.                            Public (software) Library
  1219.  
  1220.          We are not the authors of this program, nor are we associated
  1221.          with the author in any way other than as a distributor of the
  1222.          program in accordance with the author's terms of distribution.
  1223.  
  1224.          Please direct shareware payments and specific questions about
  1225.          this program to the author of the program, whose name appears
  1226.          elsewhere in  this documentation. If you have trouble getting
  1227.          in touch with the author,  we will do whatever we can to help
  1228.          you with your questions. All programs have been tested and do
  1229.          run.  To report problems,  please use the form that is in the
  1230.          file PROBLEM.DOC on many of our disks or in other written for-
  1231.          mat with screen printouts, if possible.  PsL cannot debug pro-
  1232.          programs over the telephone, though we can answer questions.
  1233.  
  1234.          Disks in the PsL are updated  monthly,  so if you did not get
  1235.          this disk directly from the PsL, you should be aware that the
  1236.          files in this set may no longer be the current versions. Also,
  1237.          if you got this disk from another vendor and are having prob-
  1238.          lems,  be aware that  some files may have become corrupted or
  1239.          lost by that vendor. Get a current, working disk from PsL.
  1240.  
  1241.          For a copy of the latest monthly software library newsletter
  1242.          and a list of the 4,000+ disks in the library, call or write
  1243.  
  1244.                            Public (software) Library
  1245.                                P.O.Box 35705 - F
  1246.                             Houston, TX 77235-5705
  1247.  
  1248.                                 1-800-2424-PSL
  1249.                              MC/Visa/AmEx/Discover
  1250.  
  1251.                           Outside of U.S. or in Texas
  1252.                           or for general information,
  1253.                               Call 1-713-524-6394
  1254.  
  1255.  
  1256.